package com.docmgmt.mvp.controller;

import com.docmgmt.mvp.dto.*;
import com.docmgmt.mvp.entity.FileEntity;
import com.docmgmt.mvp.entity.FolderEntity;
import com.docmgmt.mvp.exception.UnauthorizedException;
import com.docmgmt.mvp.mapper.FileMapper;
import com.docmgmt.mvp.mapper.FolderMapper;
import com.docmgmt.mvp.service.PermissionService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Search Controller
 *
 * @author Dora
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/search")
@RequiredArgsConstructor
public class SearchController {

    private final FileMapper fileMapper;
    private final FolderMapper folderMapper;
    private final PermissionService permissionService;

    /**
     * 5.1 Global Search
     *
     * GET /api/v1/search
     */
    @GetMapping
    public Result<PagedResult<SearchResultVO>> globalSearch(@Valid SearchRequest request) {
        Long userId = getCurrentUserId();

        log.info("GET /api/v1/search - userId={}, keyword={}, type={}",
                userId, request.getKeyword(), request.getType());

        List<SearchResultVO> results = new ArrayList<>();

        // Search files
        if (request.getType() == null || "file".equals(request.getType())) {
            List<FileEntity> files = fileMapper.searchByName(request.getKeyword(), userId);
            
            // Filter by extension if provided
            if (request.getExtension() != null && !request.getExtension().isEmpty()) {
                files = files.stream()
                        .filter(f -> request.getExtension().equalsIgnoreCase(f.getFileExtension()))
                        .collect(Collectors.toList());
            }

            // Convert to search results
            results.addAll(files.stream()
                    .filter(file -> permissionService.checkPermission(userId, "file:" + file.getId(), "read"))
                    .map(file -> fileToSearchResult(file))
                    .collect(Collectors.toList()));
        }

        // Search folders
        if (request.getType() == null || "folder".equals(request.getType())) {
            List<FolderEntity> folders = folderMapper.searchByName(request.getKeyword());
            
            results.addAll(folders.stream()
                    .filter(folder -> permissionService.checkPermission(userId, "folder:" + folder.getId(), "read"))
                    .map(folder -> folderToSearchResult(folder))
                    .collect(Collectors.toList()));
        }

        // Manual pagination
        int total = results.size();
        int start = (request.getPage() - 1) * request.getPageSize();
        int end = Math.min(start + request.getPageSize(), total);

        List<SearchResultVO> pageResults = start < total ?
                results.subList(start, end) : new ArrayList<>();

        PagedResult<SearchResultVO> pagedResult = PagedResult.<SearchResultVO>builder()
                .items(pageResults)
                .pagination(PagedResult.Pagination.builder()
                        .page(request.getPage())
                        .pageSize(request.getPageSize())
                        .total((long) total)
                        .totalPages((int) Math.ceil((double) total / request.getPageSize()))
                        .build())
                .build();

        return Result.success("Search completed", pagedResult);
    }

    /**
     * 5.2 Advanced Search
     *
     * POST /api/v1/search/advanced
     */
    @PostMapping("/advanced")
    public Result<PagedResult<SearchResultVO>> advancedSearch(@Valid @RequestBody SearchRequest request) {
        Long userId = getCurrentUserId();

        log.info("POST /api/v1/search/advanced - userId={}, keyword={}", userId, request.getKeyword());

        // For now, use the same logic as global search
        // In the future, can add more advanced filters like date range, size range, tags, etc.
        return globalSearch(request);
    }

    /**
     * Convert FileEntity to SearchResultVO
     */
    private SearchResultVO fileToSearchResult(FileEntity file) {
        return SearchResultVO.builder()
                .type("file")
                .id(file.getId())
                .name(file.getFileName())
                .path(file.getFilePath())
                .size(file.getFileSize())
                .sizeDisplay(formatFileSize(file.getFileSize()))
                .extension(file.getFileExtension())
                .mimeType(file.getMimeType())
                .creatorId(file.getUploadedBy())
                .creatorName("Unknown User")
                .createdAt(file.getCreatedAt() != null ?
                        file.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null)
                .updatedAt(file.getUpdatedAt() != null ?
                        file.getUpdatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null)
                .score(1.0)
                .build();
    }

    /**
     * Convert FolderEntity to SearchResultVO
     */
    private SearchResultVO folderToSearchResult(FolderEntity folder) {
        return SearchResultVO.builder()
                .type("folder")
                .id(folder.getId())
                .name(folder.getName())
                .path(folder.getPath())
                .creatorId(folder.getCreatorId())
                .creatorName("Unknown User")
                .createdAt(folder.getCreatedAt() != null ?
                        folder.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null)
                .updatedAt(folder.getUpdatedAt() != null ?
                        folder.getUpdatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null)
                .score(1.0)
                .build();
    }

    /**
     * Format file size
     */
    private String formatFileSize(Long size) {
        if (size == null || size == 0) {
            return "0 B";
        }
        String[] units = {"B", "KB", "MB", "GB", "TB"};
        int unitIndex = 0;
        double sizeInUnit = size.doubleValue();

        while (sizeInUnit >= 1024 && unitIndex < units.length - 1) {
            sizeInUnit /= 1024;
            unitIndex++;
        }

        return String.format("%.2f %s", sizeInUnit, units[unitIndex]);
    }

    /**
     * Get current user ID from Spring Security context
     */
    private Long getCurrentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw new UnauthorizedException("Not logged in or authentication expired");
        }

        Object principal = authentication.getPrincipal();
        if (principal instanceof Long) {
            return (Long) principal;
        }

        throw new UnauthorizedException("Invalid authentication information");
    }
}
